export { }

class Person {
  name: string = '123';
  getName(): void {
    console.log(this.name)
  }
}
let p1 = new Person()
p1.name = 'smd'
p1.getName()

// 定义存取器
class User {
  // myName: string;
  // constructor(myName: string) {
  //   this.myName = myName
  // }
  // 简便写法
  constructor(public myName: string) { // 使用public关键字 相当于在当前类中添加了myName属性 

  }
  get name() {
    return this.myName
  }
  set name(val) {
    this.myName = val
  }
}
let user = new User('smd')
user.name = '123'
console.log(user.name) // 123

// readonly 的用法
class Animal {
  public readonly name: string
  constructor(name: string) {
    this.name = name
  }
  getName(name: string) {
    // 错误 this.name = name // 无法分配到 "name" ，因为它是只读属性。  
  }
}

// 继承  // 包括 public protected private
class Person1 {
  public name: string; // 公开的  自己 自己的子类 和其他类都可以访问
  protected age: number // 受保护的  自己  自己的子类可以访问  其他类不行
  private money: number // 私有的 只有自己可以访问
  static toString() { // 静态的 子类可以访问父类的静态方法 也可以重名  会覆盖父类的方法
    console.log(
      'person1'
    );
  }
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  getName(): string {
    return this.name
  }
  setName(name: string): void {
    this.name = name
  }
}

class Student extends Person1 {
  stuNo: number
  static toString() {
    console.log(
      'Student'
    );
  }
  constructor(name: string, age: number, stuNo: number) {
    super(name, age)
    this.stuNo = stuNo
  }
  getStuNo(): number {
    return this.stuNo
  }
}
let s1 = new Student('smd', 18, 1)
console.log(s1.name);
// 错误 console.log(s1.age); // 属性“age”受保护，只能在类“Person1”及其子类中访问


// 装饰器

// 类装饰器
function addNameEat(constructor: Function) {
  constructor.prototype.name = 'smd'
  constructor.prototype.eat = function () { }
}

@addNameEat
class Person2 { // 对修饰器的实验支持功能在将来的版本中可能更改。在 "tsconfig" 或 "jsconfig" 中设置 "experimentalDecorators" 选项以删除此警告。
  name: string
  eat: Function
  constructor() { }
}
let p: Person2 = new Person2()
console.log(p.name);
p.eat()

// 装饰器工厂
function addNameEatFactory(name: string) {
  return function addNameEat(constructor: Function) {
    constructor.prototype.name = name
    constructor.prototype.eat = function () { }
  }
}

@addNameEatFactory('shuaige')
class Person3 {
  name: string
  eat: Function
}
let p3: Person3 = new Person3()
console.log(p3.name);
p3.eat()


namespace c {
  // 装饰器函数中的属性  只能多  不能少
  function repalceClass(constructor: Function) {
    return class {
      name: string
      age: number
      eat: Function
      constructor() { }
    }
  }

  @repalceClass
  class Preson {
    name: string = 'smd'
    age: number
    constructor() { }
  }
  let p1: Preson = new Preson()
  console.log(p1.name)

}

// 属性装饰器   1.装饰属性    2.装饰方法
namespace d {
  /**
   * 
   * @param target 如果装饰的是实例属性  则target表示实例的原形
   * @param propertyKey 属性名
   */
  function upperCase(target: any, propertyKey: string) {
    console.log(target, propertyKey) // { getName: [Function (anonymous)], sum: [Function (anonymous)] } name （是Proson的原形）
    let value = target[propertyKey]
    const getter = () => value
    const setter = (newVal: string) => {
      value = newVal.toLocaleUpperCase()
    }

    if (delete target[propertyKey]) {
      Object.defineProperty(target, propertyKey, {
        get: getter,
        set: setter,
        enumerable: true,
        configurable: true
      })
    }
  }

  /**
   * 
   * @param target 如果装饰的是静态属性  则target表示的是构造函数本身
   * @param propertyKey 属性名 
   */
  function staticPropertyDecorator(target: any, propertyKey: string) {
    console.log(target, propertyKey) // [Function: Person] { age: 18 } age （Person本身）
  }

  /**
   * 
   * @param target 如果装饰的是实例方法  则target表示的是构造函数的原形
   * @param propertyKey 方法名 
   */
  function noEnumerable(target: any, propertyKey: string, discriptor: PropertyDescriptor) {
    console.log('noEnumerable', target)
    console.log('noEnumerable', propertyKey)
    discriptor.enumerable = false
  }


  /**
 * 
 * @param target 如果装饰的是实例方法  则target表示的是构造函数的原形
 * @param propertyKey 方法名 
 */
  function toNumber(target: any, propertyKey: string, discriptor: PropertyDescriptor) {
    console.log('toNumber', target)
    console.log('toNumber', propertyKey)
    let oldMethod = discriptor.value // 拿到老的方法
    discriptor.value = function (...args: any[]) { // 重写这个方法  将参数做处理
      args = args.map(item => parseFloat(item))
      return oldMethod.apply(this, args) // 用处理之后的入参  执行原来的方法
    }
  }

  class Person {
    @upperCase
    name: string = 'smd'  // 实例属性
    @staticPropertyDecorator
    public static age: number = 18 // 静态属性
    constructor() { }
    @noEnumerable
    getName() { // 实例方法
      console.log(this.name)
    }
    @toNumber
    sum(...args: any[]) { // 实例方法
      return args.reduce((accu: number, item: number) => accu + item, 0)
    }
  }
  let p = new Person()
  console.log(p.sum('1', '2'))
}

// 参数装饰器
namespace e {
  // 在IOC容器中大放异彩 nest.js中大量使用参数装饰器
  /**
   * 
   * @param target 静态成员时表示构造函数   非静态成员表示构造函数原形
   * @param methodsName 参数名称 login
   * @param paramIndex 参数索引
   */
  function addAge(target: any, methodsName: string, paramIndex: number) {
    console.log(target, methodsName, paramIndex);
    target.age = 10
  }
  class Person {
    age: number
    login(userName: string, @addAge password: string) {
      console.log(this.age, userName, password);
    }
  }
  let p = new Person()
  p.login('smd', 'sss') // 10 smd sss
}

// 装饰器的执行顺序
namespace f {
  function classDecorator1() {
    return function (target: any) {
      console.log('classDecorator1')
    }
  }
  function classDecorator2() {
    return function (target: any) {
      console.log('classDecorator2')
    }
  }

  function PropertyDecorator(name: string) {
    return function (target: any, propertyName: any) {
      console.log('PropertyDecorator', propertyName, name)
    }
  }

  function MeghodDecorator() {
    return function (target: any, propertyName: any) {
      console.log('MeghodDecorator', propertyName)
    }
  }
  function ParameterDecrator() {
    return function (target: any, methodName: any, paramIndex: any) {
      console.log('ParameterDecrator', methodName, paramIndex)
    }
  }


  @classDecorator1()
  @classDecorator2()
  class Person {
    @PropertyDecorator('name')
    name: string = ''
    @PropertyDecorator('age')
    age: number = 10
    @MeghodDecorator()
    hello(@ParameterDecrator() p1: string, @ParameterDecrator() p2: string) {
    }
  }

  /*
  执行顺序规律
  PropertyDecorator name name
  PropertyDecorator age age
  ParameterDecrator hello 1
  ParameterDecrator hello 0
  MeghodDecorator hello
  classDecorator2
  classDecorator1

  1.类装饰器最后执行  后写的先执行（挨着类越近  越先执行）
  2.方法装饰器和参数装饰器同时存在时   先执行参数装饰器
  3.方法装饰器和属性装饰器    从上到下执行
  */
}

// 抽象类
namespace g {
  abstract class Animal { // 抽象类不能被实例化
    name: string
    abstract speak(): void
  }
  // 多态： 同一个方法 不同的子类有不同的实现
  class Cat extends Animal { // 继承的子类必须实现父类的抽象方法
    speak(): void {
      console.log('喵喵')
    }
  }

  class Dog extends Animal { // 继承的子类必须实现父类的抽象方法
    speak(): void {
      console.log('汪汪')
    }
  }

  /**
   * 重写(override) 子类重写  继承父类方法
   * 重载(overload) 函数的重载
   */
  function double(val: string): any
  function double(val: number): any
  function double(val: any) {
    if (typeof val === 'string') {
      return val + val
    } else if (typeof val === 'number') {
      return val * 2
    }
  }
  double('1')
  double(1)
  // 错误 double(true) // 在函数不重载之前  可以传入除string  number之外的类型  但是重载之后就不行了
}

